package com.martin.lib_base.ktx

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewModelScope
import com.google.gson.Gson
import com.martin.lib_base.base.BaseResponse
import com.martin.lib_base.interfaces.ILoading
import com.martin.lib_base.interfaces.IRelease
import com.martin.lib_base.utils.ExceptionUtil
import com.martin.lib_base.utils.addReleaseForKey
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody

/**
 * @desc：网络请求封装
 */

/**
 * 请求回调类
 */
class ResultBuilder<T> {
    /**
     * 请求成功
     */
    var onSuccess: (data: BaseResponse<T>) -> Unit = {}

    /**
     * 请求失败
     */
    var onFailed: (code: Int, message: String) -> Unit = { _, message -> errorToast(message) }

    /**
     * 请求完成
     */
    var onComplete: () -> Unit = {}
}

/**
 * 构建请求参数
 */
fun buildRequest(module: String = "", `interface`: Any? = null, data: Any? = null): RequestBody {
    val gson = Gson()
    val map = mutableMapOf(
        "source" to "android",
        "version" to "v1",
        "module" to module,
        "interface" to `interface`,
    )
    data?.let {
        map["data"] = it
    }

    return gson.toJson(map)
        .toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull())
}

/**
 * 分析结果,执行回调
 */
private suspend fun <T> parseResultAndCallback(
    response: BaseResponse<T>,
    resultBuilder: ResultBuilder<T>.() -> Unit
) {
    withContext(Dispatchers.Main) {
        val listener = ResultBuilder<T>().also(resultBuilder)
        when (response.code) {
            //请求正常
            200, 0 -> {
                listener.onSuccess(response)
            }
            //其他异常
            else -> {
                listener.onFailed(response.code, response.msg)
            }
        }
    }
}

/**
 * 创建异常处理器
 * 统一处理异常
 */
private fun <T> createExceptionHandler(listenerBuilder: ResultBuilder<T>.() -> Unit): CoroutineExceptionHandler {
    return CoroutineExceptionHandler { _, throwable ->
        val listener = ResultBuilder<T>().also(listenerBuilder)
        listener.onFailed(-1, ExceptionUtil.catchException(throwable))
    }
}

/**
 * 启动请求并收集数据
 * 有loading加载框
 * @NOTE: 如果需要自动取消请求,需要实现[IRelease]接口,并且在合适的时机调用release方法
 */
fun <T> ILoading.launchLoadingCollect(
    block: suspend () -> BaseResponse<T>,
    resultBuilder: ResultBuilder<T>.() -> Unit
) {
    when (this) {
        is LifecycleOwner -> this.lifecycleScope
        is ViewModel -> viewModelScope
        else -> GlobalScope
    }.launch(
        createExceptionHandler(resultBuilder)
    ) {
        flow {
            emit(block())
        }.onStart {
            withContext(Dispatchers.Main) {
                showLoading()
            }
        }.onCompletion {
            withContext(Dispatchers.Main) {
                dismissLoading()
                val listener = ResultBuilder<T>().also(resultBuilder)
                listener.onComplete()
            }
        }.collect {
            parseResultAndCallback(it, resultBuilder)
        }
    }.apply {
        if (this@launchLoadingCollect is IRelease) {
            addReleaseForKey(this@launchLoadingCollect) {
                cancel()
            }
        }
    }
}

/**
 * 启动请求并收集数据
 * 没有loading加载框
 * @NOTE: 如果需要自动取消请求,需要实现[IRelease]接口,并且在合适的时机调用release方法
 */
fun <T> Any.launchCollect(
    block: suspend () -> BaseResponse<T>,
    resultBuilder: ResultBuilder<T>.() -> Unit
) {
    when (this) {
        is LifecycleOwner -> this.lifecycleScope
        is ViewModel -> viewModelScope
        else -> GlobalScope
    }.launch(
        createExceptionHandler(resultBuilder)
    ) {
        flow {
            emit(block())
        }.collect {
            parseResultAndCallback(it, resultBuilder)
        }
    }.apply {
        if (this@launchCollect is IRelease) {
            addReleaseForKey(this@launchCollect) {
                cancel()
            }
        }
    }
}
